package edu.gu.hajo.gcl.parser;

import java.util.ArrayList;
import java.util.List;

import edu.gu.hajo.gcl.exception.ParseException;
import edu.gu.hajo.gcl.interp.IInterpreter;
import edu.gu.hajo.gcl.script.Script;

/**
 * Parsing a complete command
 * @author hajo
 * 
 */
public final class CommandParser {

	public IInterpreter interp;
	BracesParser bracesParser;
	VarParser varParser;
	CmdSubstParser cmdSubstParser;
	StringParser stringParser;

	public CommandParser(IInterpreter interp) {
		this.interp = interp;
	}

	public CommandParser(IInterpreter interp, BracesParser bracesParser,
			VarParser varParser, CmdSubstParser cmdSubstParser,
			StringParser stringParser) {
		this.interp = interp;
		this.bracesParser = bracesParser;
		this.varParser = varParser;
		this.cmdSubstParser = cmdSubstParser;
		this.stringParser = stringParser;

	}

	public List<String> parse(Script script) {

		String word = null;
		List<String> words = new ArrayList<String>();

		Parser.parseSpace(script);

		char ch;
		while ((ch = script.next()) != 0) {
			switch (ch) {
			case '#':// Comments skipped by ScriptFileReader
				throw new ParseException("Found a comment, should not happend");
			case '{':
				script.putBack();
				word = bracesParser.parse(script);
				/*
				 * Must be a space after else we can't
				 * destinguish if {...} {...} (2 braces) from
				 * if { ..}{.. }, (1 braces)
				 */
				if (!Character.isWhitespace(script.next())) {
					throw new ParseException("Extra chars after }");
				}
				script.putBack();
				break;
			case '[':
				script.putBack();
				String cmd = cmdSubstParser.parse(script);
				// TODO comment to test without eval
				interp.eval(new Script(cmd));
				word = interp.getResult();
				break;
			case '"':
				script.putBack();
				// quoteParser.parse(script, word);
				// TODO chech space after
				break;
			case '$':
				script.putBack();
				String name = varParser.parse(script);
				word = interp.getVar(name);
				break;
			// case ']':
			case ' ':
			case '\r':
			case '\t':
				words.add(word);
				word = null; // Important see default
				Parser.parseWordSeparator(script);
				break;
			case ';':
			case '\n':
				/*
				 * Sometimes command ends in space + \n. Space will add the
				 * word, AND null it (see above). Spaces removed but not \n
				 */
				if (word != null) {
					words.add(word);
				}
				Parser.parseCmdSeparator(script);
				return words;
			default:
				/*
				 * It's possible to have a word here if string started with {}, $
				 * or []
				 */
				String pre = word;
				script.putBack();
				word = stringParser.parse(script);
				if (pre != null) {
					word = pre + word;
				}
			}
		}
		/*
		 * If script ended loop. Example [ + 1 1] -> "+ 1 1" NO word separator!
		 */
		if (word != null) {
			words.add(word);
		}
		return words;
	}
}
